package com.jse.web;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpCookie;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

import com.jse.Awts;
import com.jse.Fs;
import com.jse.Http;
import com.jse.Jse;
import com.jse.Lang;
import com.jse.json.Json;
import com.jse.tpl.Tpl;
import com.sun.net.httpserver.HttpExchange;

public interface View {
	
	void render(HttpExchange ex,Object o) throws IOException;

	View captcha=(ex,code)->{
		ex.getResponseHeaders().add("Content-Type","image/jpeg");
		ex.getResponseHeaders().add("Pragma","No-cache");
		ex.getResponseHeaders().add("Cache-Control", "no-cache");
		ex.getResponseHeaders().add("Expires","0");
		Web.session(ex).setAttribute("captcha",code);
		Awts.captcha(code.toString(),ex.getResponseBody());
	};

	
	View err=(ex,o)->{
		var bt=o.toString().getBytes();
		ex.sendResponseHeaders(500,bt.length);
		ex.getResponseBody().write(bt);
	};
	View redirect=(ex,url)->{
		ex.getResponseHeaders().add("Location",url.toString());
		ex.sendResponseHeaders(302,-1);
	};
	View forward=(ex,url)->{
		//重新执行一次handler
		ex.getResponseHeaders().add("Location",url.toString());
		ex.sendResponseHeaders(301,-1);
	};
	View staticforward=(ex,url)->{
		var path=Path.of(Jse.webapp(ex.getLocalAddress().getHostName()),url.toString());
		var is=Files.exists(path);
		if(!is)path=Path.of(Jse.webapp(ex.getLocalAddress().getHostName()),"static",url.toString());
		if(Files.exists(path)){
			long ml=System.currentTimeMillis();
			ex.getResponseHeaders().add("Last-Modified",ml+"");
			ex.getResponseHeaders().add("Expires",new Date(ml+864000000L).toString());
			ex.getResponseHeaders().add("Content-Type",Fs.mimeType(path,"text/plain"));
			String ifModifiedSince = ex.getRequestHeaders().getFirst("If-Modified-Since");
			var ftime=Files.getLastModifiedTime(path);
			var lastModified=ftime.toMillis();
	        ZonedDateTime last = ftime.toInstant().atZone(ZoneId.systemDefault());
	        String eTag = "\"" + lastModified + "-" + path.hashCode()+"\"";
	        ex.getResponseHeaders().add("ETag", eTag);
	        if(eTag.equals(ex.getRequestHeaders().getFirst("If-Modified-Since")) && ifModifiedSince != null
		              && !ZonedDateTime.parse(ifModifiedSince, DateTimeFormatter.RFC_1123_DATE_TIME)
		              .isBefore(last)) {
	        	ex.sendResponseHeaders(304,0);
		        return;
		    }
	        ex.getResponseHeaders().add("Last-Modified", DateTimeFormatter.RFC_1123_DATE_TIME.format(last));
	        ex.sendResponseHeaders(200,0);
			Files.copy(path,ex.getResponseBody());
			return;
		}
		ex.sendResponseHeaders(404,0);
//		not.render(req,resp,null);
	};
	View not=(ex,o)->{
		if(Web.existsNot())staticforward.render(ex,"/404.html");
		else ex.sendResponseHeaders(404,-1);
	};
	View tpl=(ex,path)->{
//		System.out.println("tpl="+11);
		ex.getResponseHeaders().add("Content-Type","text/html;charset=UTF-8");
//		var s=Tpl.use().getTemplate((String)path,false).renderToString(Web.datas(ex));
//		System.out.println("s="+s);
//		var bt=s.getBytes();
		ex.sendResponseHeaders(200,0);
//		ex.getResponseBody().write(bt);
		Tpl.use().getTemplate((String)path,false)
		.render(Web.datas(ex.getHttpContext().getAttributes()),ex.getResponseBody());
	};

	View html=(ex,h)->{
		ex.getResponseHeaders().add("Content-Type","text/html;charset=UTF-8");
		try (var out = ex.getResponseBody()) {
			if(h==null) {
				out.write('\0');
			}else if(h instanceof Path p) {
				out.write(Fs.readString(p).getBytes());
			}else{
				var s=h.toString();
				if(s.startsWith("http://")||s.startsWith("https://")) {
					out.write(Http.get(s).getBytes());
				}else {
					out.write(s.getBytes());
				}
			}
		}
	};

	View json=(ex,obj)->{
		ex.getResponseHeaders().add("Content-Type","application/json;charset=UTF-8");
		var b=obj==null?new byte[0]:Json.toJson(obj).getBytes();
		ex.sendResponseHeaders(200,b.length);
		try (var out = ex.getResponseBody()) {
			out.write(b);
		}
	};
	View text=(ex,s)->{
		ex.getResponseHeaders().add("Content-Type","text/plain;charset=UTF-8");
		ex.sendResponseHeaders(200,0);
		try (var out = ex.getResponseBody()) {
		   out.write(((String)s).getBytes());
		}
	};
	View xml=(ex,s)->{
		ex.getResponseHeaders().add("Content-Type","text/xml;charset=UTF-8");
		ex.sendResponseHeaders(200,0);
		try (var out = ex.getResponseBody()) {
			out.write(((String)s).getBytes());
		}
	};
	
	View str=(ex,obj)->{//针对字符串视图,只根据返回值无其他因素
		var s=((String)obj).trim();
		var c=s.charAt(0);
		if(c=='{'||c=='[') {
			json.render(ex,s);
		}else if(c=='/') {
			redirect.render(ex,s);
		}else if(s.startsWith("<?xml")) {
			xml.render(ex,s);
		}else if(c=='<') {
			html.render(ex,s);
		}else if(s.startsWith("http://")||s.startsWith("https://")) {//代理
			redirect.render(ex,s);
		}else if(s.startsWith("->:")) {
			forward.render(ex,s.substring(3));
		}else if(s.startsWith(">>:")) {
			redirect.render(ex,s.substring(3));
		}else {
			text.render(ex,s);
		}
	};
//
	View filter=(ex,obj)->{//针对拦截专用视图
		if(obj==null||obj==Boolean.TRUE)return;//什么都不做放行
		if(obj instanceof String s) {//字符串视图
			str.render(ex,s);
		}else if(obj instanceof Map||obj instanceof Collection) {
			json.render(ex,obj);
		}else if(obj instanceof View v) {
			v.render(ex,null);
		}else html.render(ex,obj.toString());
	};
	
	View ret=(ex,o)->{
			var t=Lang.def(ex.getAttribute("@view"),"").toString();
			if(t==null){//没有配置视图
				if(o==null) {}
				else if(o instanceof View v) {
					v.render(ex, o);
				}
			}else {
				switch (t) {
					case "" ->System.out.println("");
					default ->{
						
					}
				}
			}
		};
	
	/**
	 * 返回数据给客户端
	 *
	 * @param response    响应对象{@link HttpServletResponse}
	 * @param in          需要返回客户端的内容
	 * @param contentType 返回的类型，可以使用{@link FileUtil#getMimeType(String)}获取对应扩展名的MIME信息
	 *                    <ul>
	 *                      <li>application/pdf</li>
	 *                      <li>application/vnd.ms-excel</li>
	 *                      <li>application/msword</li>
	 *                      <li>application/vnd.ms-powerpoint</li>
	 *                    </ul>
	 *                    docx、xlsx 这种 office 2007 格式 设置 MIME;网页里面docx 文件是没问题，但是下载下来了之后就变成doc格式了
	 *                    参考：<a href="https://my.oschina.net/shixiaobao17145/blog/32489">https://my.oschina.net/shixiaobao17145/blog/32489</a>
	 *                    <ul>
	 *                      <li>MIME_EXCELX_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";</li>
	 *                      <li>MIME_PPTX_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation";</li>
	 *                      <li>MIME_WORDX_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";</li>
	 *                      <li>MIME_STREAM_TYPE = "application/octet-stream;charset=utf-8"; #原始字节流</li>
	 *                    </ul>
	 * @param fileName    文件名，自动添加双引号
	 * @since 4.1.15
	 */
//	public static void write(final jakarta.servlet.http.HttpServletResponse response, final InputStream in, final String contentType, final String fileName) {
//		final String charset = Objects.requireNonNullElse(response.getCharacterEncoding(),"UTF-8");
//		final String encodeText = URLEncoder.encode(fileName,charset);
//		response.setHeader("Content-Disposition",
//				String.format("attachment;filename=\"%s\";filename*=%s''%s", encodeText, charset, encodeText));
//		response.setContentType(contentType);
//		write(response, in);
//	}
}
